using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Drawing.Drawing2D;
using System.ComponentModel.Design;
using LeisureCenter.Api;

namespace LeisureCenter.Controls
{
    public enum ListBoxViewStyle
    {
        List,
        LargeIcon
    }

    public partial class ListBox : System.Windows.Forms.ListBox, IFocusHandler
    {
        private const int LVM_FIRST = 0x1000;
        private const int LVM_GETCOUNTPERPAGE = (LVM_FIRST + 40);

        public ListBox()
        {
            InitializeComponent();
            UpdateStyles();
            DoubleBuffered = true;
            KeyDown += new KeyEventHandler(FocusHandler.HandleControlKeyDown);
        }

        private void ListBox_KeyPress(object sender, KeyPressEventArgs e)
        {
            if ((e.KeyChar == ' ' || e.KeyChar == 13) && SelectedItems.Count > 0)
                OnItemSelected(null);
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams createParams = base.CreateParams;
                // to remove the scrollbar
                createParams.Style &= ~0x200000;
                return createParams;
            }
        }

        private const int WM_ERASEBKGND = 0x0014;

        protected override void WndProc(ref Message m)
        {
            if (m.Msg != WM_ERASEBKGND || Items.Count == 0)
                base.WndProc(ref m);
        }

        public new void EndUpdate()
        {
            base.EndUpdate();
            RefreshScrollers();
            if (Items.Count > 0 && SelectedIndex < 0)
                SelectedIndex = 0;
        }

        private ScrollBar scrollBar;

        public ScrollBar ScrollBar
        {
            get { return scrollBar; }
            set { scrollBar = value; }
        }

        private ArrowPanel arrowPanelUp;

        public ArrowPanel ArrowPanelUp
        {
            get { return arrowPanelUp; }
            set
            {
                if (arrowPanelUp != null)
                    arrowPanelUp.Click -= new EventHandler(arrowPanelUp_Click);
                arrowPanelUp = value;
                if (arrowPanelUp != null)
                    arrowPanelUp.Click += new EventHandler(arrowPanelUp_Click);
            }
        }

        void arrowPanelUp_Click(object sender, EventArgs e)
        {
            SelectPreviousItem();
        }

        public void SelectPreviousItem()
        {
            if (Items.Count > 0)
            {
                if (SelectedIndex <= 0)
                    SelectedIndex = Items.Count - 1;
                else
                    SelectedIndex = SelectedIndex - 1;
            }
        }

        private ArrowPanel arrowPanelDown;

        public ArrowPanel ArrowPanelDown
        {
            get { return arrowPanelDown; }
            set
            {
                if (arrowPanelDown != null)
                    arrowPanelDown.Click -= new EventHandler(arrowPanelDown_Click);
                arrowPanelDown = value;
                if (arrowPanelDown != null)
                    arrowPanelDown.Click += new EventHandler(arrowPanelDown_Click);
            }
        }

        void arrowPanelDown_Click(object sender, EventArgs e)
        {
            SelectNextItem();
        }

        public void SelectNextItem()
        {
            if (Items.Count > 0)
            {
                if (SelectedIndex == Items.Count - 1)
                    SelectedIndex = 0;
                else
                    SelectedIndex = SelectedIndex + 1;
            }
        }

        private ListBoxViewStyle viewStyle;

        public ListBoxViewStyle ViewStyle
        {
            get { return viewStyle; }
            set
            {
                viewStyle = value;
                if (viewStyle == ListBoxViewStyle.LargeIcon)
                    DrawMode = DrawMode.OwnerDrawVariable;
                else
                {
                    DrawMode = DrawMode.OwnerDrawFixed;
                    ItemHeight = GetListViewStyleItemHeight();
                }
            }
        }

        private ImageList images;

        public ImageList Images
        {
            get { return images; }
            set { images = value; }
        }

        public void RefreshScrollers()
        {
            if (scrollBar != null)
            {
                scrollBar.Count = Items.Count;
                scrollBar.Position = TopIndex;
                scrollBar.VisibleCount = ClientSize.Height / ItemHeight;
            }
            if (arrowPanelUp != null)
                arrowPanelUp.Enabled = (TopIndex > 0);
            if (arrowPanelDown != null)
                arrowPanelDown.Enabled = (Items.Count > 0) && (GetItemRectangle(Items.Count - 1).Bottom > ClientSize.Height);
        }

        protected override void OnClientSizeChanged(EventArgs e)
        {
            base.OnClientSizeChanged(e);
            RefreshScrollers();
        }

        protected override void OnSelectedIndexChanged(EventArgs e)
        {
            base.OnSelectedIndexChanged(e);
            RefreshScrollers();
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Up && (SelectedIndex > 0 || this.focusUp == null))
            {
                SelectPreviousItem();
                e.Handled = true;
            }
            else
            if (e.KeyCode == Keys.Down && (SelectedIndex < Items.Count - 1 || this.focusDown == null))
            {
                SelectNextItem();
                e.Handled = true;
            }
            else
                base.OnKeyDown(e);
        }

        private void ListBox_DoubleClick(object sender, EventArgs e)
        {
            if (SelectedItem != null)
                OnItemSelected(e);
        }

        public event EventHandler ItemSelected;

        protected void OnItemSelected(EventArgs e)
        {
            if (ItemSelected != null)
                ItemSelected(this, e);
        }

        public void SelectItem()
        {
            if (SelectedItems.Count > 0)
                OnItemSelected(null);
        }

        private const int PADDING = 2;

        private void ListBox_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index < 0 || e.Index >= Items.Count)
                return;

            ListBoxItem item = (ListBoxItem)Items[e.Index];
            Rectangle bounds = new Rectangle(new Point(0, 0), e.Bounds.Size);
            using (Bitmap buffer = new Bitmap(bounds.Width, bounds.Height))
            {
                using (Graphics graphics = Graphics.FromImage(buffer))
                {
                    Rectangle textBounds = new Rectangle(bounds.Location, bounds.Size);
                    textBounds.Inflate(-3, -3);

                    bool selected = ((e.State & (DrawItemState.Selected | DrawItemState.Focus)) > 0);

                    // clear background
                    using (Brush backgroundBrush = new SolidBrush(BackColor))
                    {
                        graphics.FillRectangle(backgroundBrush, bounds);


                        // colors
                        Brush frameBrush = SystemBrushes.Window;
                        Brush textBrush = SystemBrushes.WindowText;
                        if (selected)
                        {
                            if ((e.State & DrawItemState.Focus) > 0)
                            {
                                frameBrush = SystemBrushes.ButtonShadow;
                                textBrush = SystemBrushes.Window;
                            }
                            else
                                if (e.Index == SelectedIndex && Focused)
                                    return;
                                else
                                {
                                    frameBrush = SystemBrushes.ButtonFace;
                                }
                        }

                        // selection frame
                        if (selected)
                        {
                            Panel.FillRoundRect(graphics, bounds, frameBrush);
                        }

                        // icon
                        if (images != null)
                        {
                            Rectangle iconBounds = bounds;
                            iconBounds.Inflate(-PADDING, -PADDING);
                            if (viewStyle == ListBoxViewStyle.LargeIcon)
                            {
                                iconBounds.Height = images.ImageSize.Height + PADDING * 2;
                                textBounds.Y += images.ImageSize.Height + PADDING * 4;
                                textBounds.Height -= images.ImageSize.Height + PADDING * 4;
                            }
                            else
                            {
                                iconBounds.Width = images.ImageSize.Width + PADDING * 2;
                                textBounds.X += images.ImageSize.Width + PADDING * 4;
                                textBounds.Width -= images.ImageSize.Width + PADDING * 4;
                            }

                            if (selected)
                            {
                                Panel.FillRoundRect(graphics, iconBounds, backgroundBrush);
                            }

                            iconBounds.Inflate(-PADDING, -PADDING);

                            if (viewStyle == ListBoxViewStyle.LargeIcon)
                            {
                                iconBounds.X += (iconBounds.Width - images.ImageSize.Width) / 2;
                                iconBounds.Width = images.ImageSize.Width;
                            }
                            else
                            {
                                iconBounds.Y += (iconBounds.Height - images.ImageSize.Height) / 2;
                                iconBounds.Height = images.ImageSize.Height;
                            }

                            if (images.Images.ContainsKey(item.IconName))
                                graphics.DrawImageUnscaledAndClipped(images.Images[item.IconName], iconBounds);
                        }
                        // thanks to Bob Dankert for the flicker free drawing of the listbox
                        // http://www.dotnet247.com/247reference/msgs/45/228729.aspx
                        if (e.Index == this.Items.Count - 1 && (this.Bounds.Height - (e.Bounds.Y + e.Bounds.Height) > 0))
                            e.Graphics.FillRectangle(backgroundBrush, e.Bounds.X, e.Bounds.Bottom, e.Bounds.Width, this.Bounds.Height - e.Bounds.Bottom);


                        DrawItemText(item, graphics, textBounds, textBrush);

                        e.Graphics.DrawImageUnscaledAndClipped(buffer, e.Bounds);
                    }
                }
            }
        }

        protected virtual void DrawItemText(ListBoxItem item, Graphics graphics, Rectangle textBounds, Brush textBrush)
        {
            //// text format
            //StringFormat format = new StringFormat();
            //if (viewStyle == ListBoxViewStyle.LargeIcon)
            //{
            //    format.LineAlignment = StringAlignment.Center;
            //    format.Alignment = StringAlignment.Center;
            //}
            //else
            //{
            //    format.Alignment = StringAlignment.Near;
            //    format.LineAlignment = StringAlignment.Near;
            //    format.FormatFlags |= StringFormatFlags.NoWrap;
            //}

            //// text
            //graphics.DrawString(item.Text, Font, textBrush, textBounds, format);

            // using TextRenderer
            TextFormatFlags flags;
            if (viewStyle == ListBoxViewStyle.LargeIcon)
            {
                flags = TextFormatFlags.Top | TextFormatFlags.HorizontalCenter | TextFormatFlags.WordBreak;
            }
            else
            {
                flags = TextFormatFlags.VerticalCenter | TextFormatFlags.Left;
            }

            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            TextRenderer.DrawText(graphics, item.Text, Font, textBounds, ((SolidBrush)textBrush).Color, flags);
        }

        private int GetListViewStyleItemHeight()
        {
            int result = 0;

            if (images != null)
                result = images.ImageSize.Height + PADDING * 2;

            Graphics graphics = CreateGraphics();
            result = Math.Max(result, (int)graphics.MeasureString("Wg", Font).Height);
            graphics.Dispose();

            return result + PADDING * 2;
        }

        private void ListBox_MeasureItem(object sender, MeasureItemEventArgs e)
        {
            if (e.Index < 0 || e.Index >= Items.Count)
                return;

            ListBoxItem item = (ListBoxItem)Items[e.Index];

            int result = 0;

            if (images != null)
                result += images.ImageSize.Height + PADDING * 2;

            //StringFormat format = new StringFormat();
            //format.LineAlignment = StringAlignment.Center;
            //format.Alignment = StringAlignment.Center;
            //format.FormatFlags &= ~StringFormatFlags.NoWrap;

            //result += (int)e.Graphics.MeasureString(item.Text, Font, ClientSize.Width - PADDING * 2).Height + PADDING * 2;

            result += TextRenderer.MeasureText(e.Graphics, item.Text, Font, new Size(ClientSize.Width - PADDING * 2, 1000), viewStyle == ListBoxViewStyle.LargeIcon ? TextFormatFlags.WordBreak : TextFormatFlags.Default).Height + PADDING * 2;

            e.ItemHeight = result + PADDING * 2;
            e.ItemWidth = ClientSize.Width;
            return;
        }

        private void ListBox_Enter(object sender, EventArgs e)
        {
            if (Items.Count > 0 && SelectedIndex < 0)
                SelectedIndex = 0;
        }

        #region IFocusHandler Membres

        private IFocusHandler focusLeft;
        [Category("Focus")]
        public IFocusHandler FocusLeft { get { return focusLeft; } set { if (value != this) focusLeft = value; } }

        private IFocusHandler focusRight;
        [Category("Focus")]
        public IFocusHandler FocusRight { get { return focusRight; } set { if (value != this) focusRight = value; } }

        private IFocusHandler focusUp;
        [Category("Focus")]
        public IFocusHandler FocusUp { get { return focusUp; } set { if (value != this) focusUp = value; } }

        private IFocusHandler focusDown;
        [Category("Focus")]
        public IFocusHandler FocusDown { get { return focusDown; } set { if (value != this) focusDown = value; } }

        [Browsable(false)]
        public Control Control { get { return this; } }

        public bool SetFocus(IFocusHandler source, Keys key)
        {
            return FocusHandler.SetFocus(this, source, key);
        }

        #endregion
    }

    public class ListBoxItem
    {
        protected ListBox listBox;

        public ListBoxItem(ListBox listBox, string text, string iconName)
        {
            this.listBox = listBox;
            this.text = text;
            this.iconName = iconName;
        }

        private string text;

        public string Text
        {
            get { return text; }
            set { text = value; }
        }

        private string iconName;

        public virtual string IconName
        {
            get { return iconName; }
            set { iconName = value; }
        }

        public override string ToString()
        {
            return text;
        }
    }
}
